package com.bw.module_video_detail.test_gl_surfaceview.filter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;

import com.bw.module_video_detail.R;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * @author ytx
 * @date 2025-10-20 14:37
 * @description
 */
public class FilterRenderer implements GLSurfaceView.Renderer {
    private final Context context;
    private int mProgram;
    private int mTextureId;
    private FloatBuffer vertexBuffer;
    private ShortBuffer indexBuffer;
    private int currentFilter = 0; // 当前滤镜类型

    // 顶点坐标（NDC） + 纹理坐标
    private final float[] vertices = {
            -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // 左下
            1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // 右下
            -1.0f,  1.0f, 0.0f, 0.0f, 1.0f, // 左上
            1.0f,  1.0f, 0.0f, 1.0f, 1.0f  // 右上
    };

    // 索引数据
    private final short[] indices = { 0, 1, 2, 1, 3, 2 };

    public FilterRenderer(Context context) {
        this.context = context;
    }

    public void setCurrentFilter(int filterType) {
        this.currentFilter = filterType;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        initVertexBuffer();
        loadTexture();
        compileShaders();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        // 根据当前滤镜选择片段着色器
        int fragmentShaderResId;
        switch (currentFilter) {
            case 1:
                fragmentShaderResId = R.raw.invert_fragment;
                break;
            case 2:
                fragmentShaderResId = R.raw.blur_fragment;
                break;
            case 3:
                fragmentShaderResId = R.raw.edge_fragment;
                break;
            default:
                fragmentShaderResId = R.raw.gray_fragment; // 默认灰度滤镜
        }

        // 重新编译着色器（切换滤镜时）
        compileShader(fragmentShaderResId);

        // 绑定纹理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
        GLES20.glUniform1i(GLES20.glGetUniformLocation(mProgram, "uTexture"), 0);

        // 绘制
        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES,
                indices.length,
                GLES20.GL_UNSIGNED_SHORT,
                indexBuffer
        );
    }

    private void initVertexBuffer() {
        ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);

        ByteBuffer ib = ByteBuffer.allocateDirect(indices.length * 2);
        ib.order(ByteOrder.nativeOrder());
        indexBuffer = ib.asShortBuffer();
        indexBuffer.put(indices);
        indexBuffer.position(0);
    }

    private void loadTexture() {
        int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);
        mTextureId = textures[0];

        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.a);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }

    private void compileShaders() {
        compileShader(R.raw.gray_fragment); // 默认加载灰度滤镜
    }

    private void compileShader(int fragmentShaderResId) {
        // 顶点着色器（固定）
        int vertexShader = loadShader(
                GLES20.GL_VERTEX_SHADER,
                readRawTextFile(R.raw.base_vertex_shader)
        );

        // 片段着色器（动态切换）
        int fragmentShader = loadShader(
                GLES20.GL_FRAGMENT_SHADER,
                readRawTextFile(fragmentShaderResId)
        );

        // 创建OpenGL程序
        mProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(mProgram, vertexShader);
        GLES20.glAttachShader(mProgram, fragmentShader);
        GLES20.glLinkProgram(mProgram);
        GLES20.glUseProgram(mProgram);

        // 绑定顶点数据
        int positionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        GLES20.glEnableVertexAttribArray(positionHandle);
        GLES20.glVertexAttribPointer(
                positionHandle, 3, GLES20.GL_FLOAT, false,
                5 * 4, vertexBuffer
        );

        // 绑定纹理坐标
        int texCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
        GLES20.glEnableVertexAttribArray(texCoordHandle);
        GLES20.glVertexAttribPointer(
                texCoordHandle, 2, GLES20.GL_FLOAT, false,
                5 * 4, vertexBuffer
        );
    }

    private int loadShader(int type, String shaderCode) {
        int shader = GLES20.glCreateShader(type);
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

    private String readRawTextFile(int resId) {
        InputStream inputStream = context.getResources().openRawResource(resId);
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            reader.close();
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}